11. 完成情感分析 RNN

参考 Solution 代码

要详细查看 solution 代码,请转到 solution workspace 或在线查看

完成 RNN 类

一定自己去尝试定义该模型并使代码能正常运行!要勤学多练哟!下面说说我是如何定义该模型的。

该模型包括一个嵌入层、一个递归层以及一个最终线性层级,并应用 S 型函数;我在 __init__ 函数中根据传入的参数定义了所有这些层级。

def __init__(self, vocab_size, output_size, embedding_dim, hidden_dim, n_layers, drop_prob=0.5):
        """
        Initialize the model by setting up the layers.
        """
        super(SentimentRNN, self).__init__()

        self.output_size = output_size
        self.n_layers = n_layers
        self.hidden_dim = hidden_dim

        # embedding and LSTM layers
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, n_layers, 
                            dropout=drop_prob, batch_first=True)

        # dropout layer
        self.dropout = nn.Dropout(0.3)

        # linear and sigmoid layers
        self.fc = nn.Linear(hidden_dim, output_size)
        self.sig = nn.Sigmoid()

__init__ 函数

首先是一个嵌入层,参数包括词汇表的大小(即整数标记的数量),并生成一个大小为 embedding_dim 的嵌入向量。在模型训练过程中,此函数将创建一个嵌入查询表,行数等于整数标记的数量,列数等于嵌入维度。

然后是一个 LSTM 层级,参数为大小是 embedding_dim 的输入。它将嵌入向量当做输入,并生成一个输出和某个隐藏大小的隐藏状态。我还指定了几个层级和丢弃值,最后将 batch_first 设为 True,因为我们要使用数据加载器批处理数据。

然后将 LSTM 输出传入丢弃层,接着是全连接线性层级,该线性层级将生成 output_size 个输出。最后定义一个 S 型层级,将输出转换为 0-1 之间的值。

前馈行为

接着定义 forward 函数,它的参数包括输入 xhidden 状态,我将按顺序将输入传入这些层级。

def forward(self, x, hidden):
        """
        Perform a forward pass of our model on some input and hidden state.
        """
        batch_size = x.size(0)

        # embeddings and lstm_out
        embeds = self.embedding(x)
        lstm_out, hidden = self.lstm(embeds, hidden)

        # stack up lstm outputs
        lstm_out = lstm_out.contiguous().view(-1, self.hidden_dim)

        # dropout and fully-connected layer
        out = self.dropout(lstm_out)
        out = self.fc(out)

        # sigmoid function
        sig_out = self.sig(out)

        # reshape to be batch_size first
        sig_out = sig_out.view(batch_size, -1)
        sig_out = sig_out[:, -1] # get last batch of labels

        # return last sigmoid output and hidden state
        return sig_out, hidden

forward 函数

首先获取输入 x 的 batch_size,用于变形数据。然后将 x 传入嵌入层,生成嵌入向量

将这些嵌入和隐藏状态传入 LSTM 层级,返回一个 lstm_output 和新的 hidden 状态。然后将 LSTM 的输出堆叠起来并传入最后的线性层级。

继续将变形后的 lstm_output 传入丢弃层和线性层级,返回一定数量的输出,并将这些输出传入 S 型激活函数。

对于一批输入数据,我希望仅返回最后一个 S 型激活函数输出,所以将这些输出设为 batch_size first。然后通过调用 sig_out[:, -1] 获取最后一个批次,这样就会获得最后一批标签。

最后,返回该输出和 LSTM 层级生成的隐藏状态。

init_hidden

这样就完成了 forward 函数,还有一个:init_hidden,它和之前看到的一样。LSTM 的隐藏状态和单元状态是值元组,每个值的大小为(n_layers * batch_size* hidden_dim)。将所有这些隐藏权重初始化为 0,并移到 GPU 上(如果有 GPU 的话)。

def init_hidden(self, batch_size):
        ''' Initializes hidden state '''
        # Create two new tensors with sizes n_layers x batch_size x hidden_dim,
        # initialized to zero, for hidden state and cell state of LSTM
        weight = next(self.parameters()).data

        if (train_on_gpu):
            hidden = (weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().cuda(),
                  weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().cuda())
        else:
            hidden = (weight.new(self.n_layers, batch_size, self.hidden_dim).zero_(),
                      weight.new(self.n_layers, batch_size, self.hidden_dim).zero_())

        return hidden

接着就可以实例化并训练此模型了,你可以通过多次尝试并找到合适的超参数,还可以在下个页面查看 solution 代码。